/*!
 * Type.js JavaScript Utility v1.0.0
 * http://typejs.liamgoodacre.com/
 * 
 * Copyright 2011, Liam Goodacre
 * 
 * Author : Liam Goodacre
 * Author URL : http://liamgoodacre.com
 * Date : 2011_07_16
 */
var Type;
Type || (Type = function () {
	var
	api,
	VALUES = {
		'Object' : 'Object',
		'Array' : 'Array',
		'String' : 'String',
		'Global' : 'Global',
		'Number' : 'Number',
		'Function' : 'Function',
		'RegExp' : 'RegExp',
		'Undefined' : 'Undefined',
		'Null' : 'Null'
	},
	undefined,
	infinity = 1/0,
	reg_global = /^Window|Process$/;
	
	return api = {
		/**
		 * Return an elements's type - look at VALUES to list of 'usual' types, others do exist (e.g. HTMLElement, etc...).
		 */
		of : function (_elem) {
			if (_elem === undefined) { return VALUES.Undefined; }
			if (_elem === null) { return VALUES.Null; }
			
			// catches IE-Browser
			if (typeof window !== 'undefined' && _elem === window) {
				return VALUES.Global;
			}
			
			return Object.prototype.toString.call(_elem).slice(8, -1).replace(/^g/, 'G').replace(reg_global, 'Global');
		},
		/**
		 * Returns:
		 * 		true : element is of type specified
		 * 		false : element is NOT of type specified
		 */
		is : function (_elem, _type) {
			return api.of(_elem) === _type;
		},
		/**
		 * Returns:
		 * 		true : elements are of the same type 
		 * 		false : elements are NOT of the same type
		 * NOTE: call as same(x) for an 'is undefined' check
		 */
		same : function (_elem, _check) {
			return api.of(_elem) === api.of(_check);
		},
		/**
		 * Returns:
		 * 		true : element is of type specified
		 * 		false : element is NOT of type specified
		 */
		isIn : function (_elem, _arrayOfElems) {
			var index = _arrayOfElems.length;
			while (index--) {
				if (api.same(_elem, _arrayOfElems[index])) {
					return true;
				}
			}
			return false;
		},
		/**
		 * Returns:
		 * 		true : element is of type specified
		 * 		false : element is NOT of type specified
		 */
		inTypes : function (_elem, _arrayOfTypes) {
			var index = _arrayOfTypes.length;
			while (index--) {
				if (api.is(_elem, _arrayOfTypes[index])) {
					return true;
				}
			}
			return false;
		},
		/**
		 * Returns:
		 *		true : element is undefined
		 * 		false : element is NOT undefined
		 */
		isUndefined : function (_elem) {
			return api.is(_elem, VALUES.Undefined);
		},
		/**
		 * Returns:
		 * 		element(_elem) : if types do match
		 * 		default(_default) : if type do NOT match
		 */
		force : function (_elem, _default, _falseValue) {
			return api.of(_elem) === api.of(_default) ? _elem : (arguments.length > 2 ? _falseValue : _default);
		},
		/**
		 * Returns:
		 *		element(_elem) : if type is same as values in
		 */
		forceIn : function (_elem, _arrayOfElems, _falseValue) {
			return (api.isIn(_elem, _arrayOfElems)) ? _elem : _falseValue;
		},
		/**
		 * Returns:
		 *		element(_elem) : if type is same as elem in array
		 */
		forceInTypes : function (_elem, _arrayOfTypes, _falseValue) {
			var index = _arrayOfTypes.length;
			while (index--) {
				if (api.is(_elem, _arrayOfTypes[index])) {
					return _elem;
				}
			}
			return _falseValue;
		},
		/**
		 * Returns a finite non-NaN number:
		 * 		element(_elem) : if finite non-NaN number
		 * 		default(_default) : if criteria is not met, and default is numeric
		 * 		0 : if default is non-numeric
		 */
		forceNum : function (_elem, _default) {
			if (api.is(_elem, VALUES.Number) && isFinite(_elem)) {
				return _elem;
			}
			return api.force(_default, 0);
		},
		/**
		 * Forces each data in an array with their corresponding default in a defaults array.
		 */
		forceAll : function (_data, _defaults) {
			var data, key;
			data = _data || {};
			for (key in _defaults) {
				data[key] = api.force(data[key], _defaults[key]);
			}
			return data;
		},
		// Undefined -> undefined
		Undefined : undefined,
		// Infinity -> infinity
		Infinity : infinity,
		// $ (valueList) -> VALUES[]
		$ : VALUES
	};
}());